import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig } from "axios";
import type { WRequestInterceptors, WRequestConfig } from "./type";
import { ElLoading } from "element-plus";
import { LoadingInstance } from "element-plus/lib/components/loading/src/loading";
class WRequest {
  instance: AxiosInstance; //创建变量，AxiosInstance类型来自axios库。
  interceptors?: WRequestInterceptors; //创建变量，WRequestInterceptors就是在上面定义的拦截器类型
  loading?: LoadingInstance; //创建变量用来保存ElLoading.servise 的返回值
  showLoading: boolean; //判断是否需要启用loading插件

  constructor(config: WRequestConfig) {
    this.instance = axios.create(config);
    this.interceptors = config.interceptors; //报存传过来的拦截器对象参数
    this.showLoading = config.showLoading ?? true; //如果config中没有showLoading，则为true

    // 1、从config中取出的拦截器是对应实例的拦截器（这里是new一个实例对象时就传进来的参数）
    //调用实例里面的interceptors.request.use方法
    this.instance.interceptors.request.use(
      // 获取this.interceptors里面的值作为参数
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    );
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    );

    //2给所有实例都添加拦截器 （这里是不需要传值，所用new出来的实例都会执行的）
    this.instance.interceptors.request.use(
      (config) => {
        // if (this.showLoading) {
        //   this.loading = ElLoading.service({
        //     lock: true,
        //     body: true,
        //     fullscreen: true,
        //     text: "Loading",
        //     background: "rgba(0,0,0,0.5)",
        //     customClass: "laoding-class",
        //   });
        // }

        return config;
      },
      (err) => {
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (res) => {
        setTimeout(() => {
          this.loading?.close();
        }, 1000);

        return res.data; //返回服务器返回的数据
      },
      (err) => {
        this.loading?.close();
        return err;
      }
    );
  }

  // <T> 这是让调用方法的人把类型传进来
  // 1.Promise后面跟的类型是返回值的类型
  // 2.自定义的request方法后面跟的是传过来的泛型
  // 3.axios里面的request后面的那个T
  // 上面是自己的总结
  // 1.request这里的T是调用该方法的人传进来的（总入口）=>Promise后面的T就是前面传进来的,因为调用者会知道自己得到的Promise里面是一个什么类型的对象
  // =>
  request<T>(config: WRequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 添加只有该方法才有的拦截器 （这里是不同实例的相同方法都会有的拦截器，参数是在调用方法时传进来的）
      if (config.interceptors?.requestInterceptor) {
        // 下面这个表达式的作用就是 执行了config.interceptors.requestInterceptor函数
        // 执行完后config会被返回 (这里我不用config接受返回的config参数行不行呢？)
        config = config.interceptors.requestInterceptor(config);
      }

      //把请求方法里面的值赋到实例里面来
      if (config.showLoading === false) {
        this.showLoading = config.showLoading;
      }

      // 这里这个any是因为需要用到的T是在第二个参数位.迫不得已补上的.
      // 这里这个T是传进来改变Axios里面返回值类型的 不传T进来就是AxiosResponse<any> 只有把T传进来才能和外面定义的一样
      this.instance
        .request<any, T>(config)
        .then((res) => {
          if (config.interceptors?.responseInterceptor) {
            // 下面这个表达式的作用就是 执行了config.interceptors.requestInterceptor函数
            // 执行完后config会被返回
            res = config.interceptors.responseInterceptor(res);
          }

          // 重置showLoading，避免影响下一个请求
          this.showLoading = true;
          resolve(res);
        })
        .catch((err) => {
          this.showLoading = true;
          reject(err);
          return err;
        });
    });
  }
  get<T>(config: WRequestConfig<T>): Promise<T> {
    return this.request({ ...config, method: "GET" });
  }
  post<T>(config: WRequestConfig<T>): Promise<T> {
    return this.request({ ...config, method: "POST" });
  }
  delete<T>(config: WRequestConfig<T>): Promise<T> {
    return this.request({ ...config, method: "DELETE" });
  }
  patch<T>(config: WRequestConfig<T>): Promise<T> {
    return this.request({ ...config, method: "PATCH" });
  }
}

export default WRequest;
